﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows;
using log4net.Appender;
using log4net.Util;
using log4net.Core;
using System.Collections;
using log4net;
using log4net.Repository.Hierarchy;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using System.Threading;

namespace TapiocaWpf.Utilities
{
    /// <summary>
    /// Description of RichTextBoxAppender.
    /// </summary>
    public class RichTextBoxAppender : AppenderSkeleton 
    {
        #region Private Instance Fields

        private RichTextBox richTextBox = null;

        public RichTextBox RichTextBox
        {
            get { return richTextBox; }
            set { richTextBox = value; }
        }

        public string WindowName { get; set; }
        public string RichTextBoxName { get; set; }

        private LevelMapping levelMapping = new LevelMapping();

        private int maxTextLength = 8000000; //8M char

        #endregion

        private delegate void UpdateControlDelegate(LoggingEvent loggingEvent);

        #region Constructor

        public RichTextBoxAppender()
            : base()
        {
            
        }

        //public RichTextBoxAppender(RichTextBox myRichTextBox) : base()
        //{
        //    richTextBox = myRichTextBox;
        //    containerForm = (Form)richTextBox.Parent.Parent.Parent.Parent.Parent;
        //}
        #endregion


        /// in the default repository with <code>Name == appenderName</code>.</para>
        /// </remarks>
        /// <example>
        /// <code lang="C#">
        /// private void MainForm_Load(object sender, EventArgs e)
        /// {
        ///    log4net.Appender.RichTextBoxAppender.SetRichTextBox(logRichTextBox, "MainFormRichTextAppender");
        /// }
        /// </code>
        /// </example>
        public static bool SetRichTextBox(RichTextBox richTextBox, string appenderName)
        {
            if (appenderName == null) return false;

            IAppender[] appenders = LogManager.GetRepository().GetAppenders();

            foreach (IAppender appender in appenders)
            {
                if (appender.Name == appenderName)
                {
                    if (appender is RichTextBoxAppender)
                    {
                        ((RichTextBoxAppender)appender).RichTextBox = richTextBox;
                        return true;
                    }
                    break;
                }
            }
            return false;
        }


        private void UpdateControl(LoggingEvent loggingEvent)
        {
            // There may be performance issues if the buffer gets too long
            // So periodically clear the buffer
            richTextBox.Dispatcher.Invoke((Action)(() =>
            {
                string content = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd).Text; 

                if (content.Length > maxTextLength)
                {
                    richTextBox.Document.Blocks.Clear();
                    richTextBox.AppendText(string.Format("(Cleared log length max: {0})\n", maxTextLength));
                }
            }));   

            // look for a style mapping
            LevelTextStyle selectedStyle = levelMapping.Lookup(loggingEvent.Level) as LevelTextStyle;

            if (selectedStyle != null)
            {
                // set the colors of the text about to be appended
                TextRange rangeOfText1 = new TextRange(richTextBox.Document.ContentEnd, richTextBox.Document.ContentEnd);
                //rangeOfText1.Text = "Text1 ";               
                System.Windows.Media.Color backColor = System.Windows.Media.Color.FromArgb(selectedStyle.BackColor.A, selectedStyle.BackColor.R, selectedStyle.BackColor.G, selectedStyle.BackColor.B);
                System.Windows.Media.Color textColor = System.Windows.Media.Color.FromArgb(selectedStyle.TextColor.A, selectedStyle.TextColor.R, selectedStyle.TextColor.G, selectedStyle.TextColor.B);

                richTextBox.Dispatcher.Invoke((Action)(() =>
                {
                    richTextBox.Background = new SolidColorBrush(backColor);
                    //richTextBox.Foreground = new SolidColorBrush(textColor);
                    //TextRange tr = new TextRange(this.richTextBox.Document.ContentEnd, this.richTextBox.Document.ContentEnd);
                    //tr.ApplyPropertyValue(RichTextBox.ForegroundProperty, new SolidColorBrush(textColor));
                    this.DisplayOnTraceBox(RenderLoggingEvent(loggingEvent), new SolidColorBrush(textColor));
                }));                

                // alter selection font as much as necessary
                // missing settings are replaced by the font settings on the control
                if (selectedStyle.Font != null)
                {
                    // set Font Family, size and styles
                    richTextBox.FontFamily = new System.Windows.Media.FontFamily(selectedStyle.Font.Name);
                }
                //else if (selectedStyle.PointSize > 0 && richTextBox.FontSize != selectedStyle.PointSize)
                //{
                //    // use control's font family, set size and styles
                //    float size = selectedStyle.PointSize > 0.0f ? selectedStyle.PointSize : (float)richTextBox.FontSize;
                //    //richTextBox.FontFamily = new System.Windows.Media.FontFamily(selectedStyle.Font.Name);
                //    //richTextBox.FontSize = size;
                //}
                //else if (!richTextBox.FontStyle.ToString().Equals(selectedStyle.FontStyle.ToString()))
                //{
                //    // use control's font family and size, set styles
                //    richTextBox.FontFamily = new System.Windows.Media.FontFamily(selectedStyle.Font.Name);
                //}
            }

            richTextBox.Dispatcher.Invoke((Action)(() =>
            {
                //richTextBox.AppendText(RenderLoggingEvent(loggingEvent));
                richTextBox.ScrollToEnd();
            })); 

            richTextBox.Dispatcher.Invoke(DispatcherPriority.Render, EmptyDelegate);
            //richTextBox.InvalidateVisual();
            //richTextBox.UpdateLayout();
        }


        private delegate void DisplayOnTraceBoxDel(object message, System.Windows.Media.Brush messageColor);

        public void DisplayOnTraceBox(object message, System.Windows.Media.Brush messageColor)
        {
            if (!this.richTextBox.Dispatcher.CheckAccess())
            {
                this.richTextBox.Dispatcher.Invoke(new DisplayOnTraceBoxDel(DisplayOnTraceBox), DispatcherPriority.DataBind, new object[] { message, messageColor });
            }
            else
            {
                TextRange tr = new TextRange(this.richTextBox.Document.ContentEnd, this.richTextBox.Document.ContentEnd);
                tr.Text = message.ToString();
                tr.ApplyPropertyValue(TextElement.FontFamilyProperty, "Microsoft Sans Serif");
                tr.ApplyPropertyValue(TextElement.FontSizeProperty, 12D);
                tr.ApplyPropertyValue(Paragraph.MarginProperty, new Thickness(0));
                //tr.ApplyPropertyValue(Paragraph.BackgroundProperty, "LightSteelBlue");
                tr.ApplyPropertyValue(TextElement.ForegroundProperty, messageColor);
                this.richTextBox.UpdateLayout();
            }
        }

        private static Action EmptyDelegate = delegate() { };

        protected override void Append(LoggingEvent LoggingEvent)
        {
            if (richTextBox == null)
            {
                if (String.IsNullOrEmpty(WindowName) ||
                    String.IsNullOrEmpty(RichTextBoxName))
                    return;

                Window window = null;

                Application.Current.Dispatcher.Invoke((Action)(() =>
                {
                    window = Application.Current.Windows.Cast<Window>().SingleOrDefault(x => x.IsActive);
                }));
         
                if (window == null)
                    return;

                Application.Current.Dispatcher.Invoke((Action)(() =>
                {
                    richTextBox = window.FindName("richTextBox1") as RichTextBox;
                }));

                if (richTextBox == null)
                    return;

                window.Closing += (s, e) => richTextBox = null;
            }

            if (richTextBox.Dispatcher.CheckAccess())
            {
                richTextBox.Dispatcher.Invoke(
                    new UpdateControlDelegate(UpdateControl),
                    new object[] { LoggingEvent });
            }
            else
            {
                UpdateControl(LoggingEvent);
            }            
        }


        public void AddMapping(LevelTextStyle mapping)
        {
            levelMapping.Add(mapping);
        }


        public override void ActivateOptions()
        {
            base.ActivateOptions();
            levelMapping.ActivateOptions();
        }

        protected override bool RequiresLayout { get { return true; } }
    }


    public class LevelTextStyle : LevelMappingEntry
    {
        private System.Drawing.Color textColor;
        private string textColorName;

        public string TextColorName
        {
            get { return textColorName; }
            set { textColorName = value; }
        }

        private System.Drawing.Color backColor;
        private System.Drawing.FontStyle fontStyle = System.Drawing.FontStyle.Regular;
        private float pointSize = 0.0f;
        private bool bold = false;
        private bool italic = false;
        private string fontFamilyName = null;
        private Font font = null;

        public System.Drawing.Color TextColor 
        { 
            get { return textColor; }
            set { textColor = value; }
        }
        public System.Drawing.Color BackColor { get { return backColor; } set { backColor = value; } }
        public System.Drawing.FontStyle FontStyle { get { return fontStyle; } set { fontStyle = value; } }
        public Font Font { get { return font; } set { font = value; } }
        public bool Bold { get { return bold; } set { bold = value; } }
        public bool Italic { get { return italic; } set { italic = value; } }
        public float PointSize 
        { 
            get { return pointSize; } 
            set { pointSize = value; }
        }

        /// <summary>
        /// Initialize the options for the object
        /// </summary>
        /// <remarks>Parse the properties</remarks>
        public override void ActivateOptions()
        {
            base.ActivateOptions();
            if (bold) fontStyle |= System.Drawing.FontStyle.Bold;
            if (italic) fontStyle |= System.Drawing.FontStyle.Italic;
            textColor = System.Drawing.Color.FromName(textColorName);

            if (fontFamilyName != null)
            {
                float size = pointSize > 0.0f ? pointSize : 8.25f;
                try
                {
                    font = new Font(fontFamilyName, size, fontStyle);
                }
                catch (Exception)
                {
                    font = new Font("Arial", 9.25f, System.Drawing.FontStyle.Regular);
                }
            }
        }        
    }

}

